package com.inspectortime.webapp.inspector.scheduleRule;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.validation.BindException;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.mvc.SimpleFormController;

import com.inspectortime.domain.Inspector;
import com.inspectortime.domain.ScheduleRule;
import com.inspectortime.domain.type.FrequencyType;
import com.inspectortime.repository.Factory;
import com.inspectortime.repository.Repository;
import com.inspectortime.util.CalendarUtil;
import com.inspectortime.webapp.util.CustomCalendarEditor;
import com.inspectortime.webapp.util.HttpSessionUtils;

public class ScheduleRuleController extends SimpleFormController {
	
	protected Factory factory;
	protected Repository repository;
	
	// TODO: Move to inspector configs	
	private int firstDayOfWeek = Calendar.SUNDAY;
	
	protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
		DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
		df.setLenient(true);
		CustomCalendarEditor editor = new CustomCalendarEditor(df, true);
		binder.registerCustomEditor(Calendar.class, editor);
	}
	
	protected Map referenceData(HttpServletRequest request) throws Exception {
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("frequencyTypes", FrequencyType.values());
		data.put("hours", buildHoursOptions());
		data.put("minutes", buildMinutesOptions());
		data.put("daysOfWeek", CalendarUtil.getDisplayableDaysOfWeekMap());
		data.put("inspectorCompany", HttpSessionUtils.retrieveLoggedInCompany(request, repository));
		return data;
	}
	
	
	/**
	 * Perform final updates to the command before submitting
	 */
	protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors) throws Exception {
		
		super.onBindAndValidate(request, command, errors);
		
		if (!errors.hasErrors()) {
		
			ScheduleRule scheduleRule = (ScheduleRule) command;
			
			//for a single day rule, set the end date to be same as start date if null,
			//so we don't persist it as a never-ending rule
			if (scheduleRule.getFrequencyType() == FrequencyType.SINGLE_DAY) {
				if (scheduleRule.getEndDate() == null) {
					scheduleRule.setEndDate(scheduleRule.getStartDate());
				}
				
				//set the units to be empty to make sure
				//we don't have unnecessary data being persisted.
				scheduleRule.setUnits(new int[0]);
			} else {
				
				//if day of week rule, null out the date fields, as we're currently not allowing dates
				//in a recurring timeslot
				scheduleRule.setStartDate(null);
				scheduleRule.setEndDate(null);
				
			}
			
			
			//now make sure there's not a conflicting scheduleRule
			String inspectorId = request.getParameter("inspectorId"); 
			Inspector inspector = repository.loadInspector(inspectorId);
			List<ScheduleRule> existingRules = inspector.getScheduleRules();
			ScheduleRule conflictingRule = null;
			
			for (ScheduleRule existingRule : existingRules) {
				if (existingRule.conflictsWith(scheduleRule)) {
					
					//if we're in 'add' mode, don't allow any conflicts
					//if we're in 'edit' mode, it's not a conflict if the 'conflicting' rule is the one we're trying to edit
					if (scheduleRule.getId() == null || 
						!scheduleRule.getId().equals(existingRule.getId())) {
						conflictingRule = existingRule;
						break;
					}
				}
			} //for
			
			if (conflictingRule != null) {
				Object[] args = new Object[2];
				args[0] = conflictingRule.getId();
				args[1] = inspectorId;
				String msg = null;
				if (conflictingRule.getFrequencyType() == FrequencyType.DAY_OF_WEEK) {
					msg = "addEditScheduleRule.conflictingRule.recurring";
				} else {
					msg = "addEditScheduleRule.conflictingRule.nonrecurring";
				}
				errors.reject(msg, args, null);
			}
		}
	}
	
	/**
	 * Converts a calendar into a string format that can be passed to 
	 * the ViewScheduleRulesController as a request param
	 * 
	 * @param cal
	 * @return
	 */
	protected String buildUrlStartDate(Calendar cal) {
		String calStr = null;
		if (cal != null) {
			cal.set(Calendar.DAY_OF_WEEK, this.firstDayOfWeek);	
			calStr = CalendarUtil.convertToNavDateString(cal.getTime());
		}
		
		return calStr;
	}
	
	private Map buildHoursOptions() {
		Map<String, String> hours = new LinkedHashMap<String, String>();
		hours.put("6", "6 am");
		hours.put("7", "7 am");
		hours.put("8", "8 am");
		hours.put("9", "9 am");
		hours.put("10", "10 am");
		hours.put("11", "11 am");
		hours.put("12", "12 pm");
		hours.put("13", "1 pm");
		hours.put("14", "2 pm");
		hours.put("15", "3 pm");
		hours.put("16", "4 pm");
		hours.put("17", "5 pm");
		hours.put("18", "6 pm");
		hours.put("19", "7 pm");
		hours.put("20", "8 pm");
		hours.put("21", "9 pm");
		return hours;
	}
	
	
	private Map buildMinutesOptions() {
		Map<String, String> minutes = new LinkedHashMap<String, String>();
		minutes.put("0", "00");
		minutes.put("15", "15");
		minutes.put("30", "30");
		minutes.put("45", "45");
		return minutes;
	}
	

	public void setFactory(Factory factory) {
		this.factory = factory;
	}
	
    public void setRepository(Repository repository) {
		this.repository = repository;
	}

	public void setFirstDayOfWeek(int firstDayOfWeek) {
		this.firstDayOfWeek = firstDayOfWeek;
	}

}
